﻿#region License
// (c) Intergen.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using TextGlow.Control.Utilities;

namespace TextGlow.Control.Model
{
  public class TableProperties : ContentProperties
  {
    private Style _definingStyle;

    private string _alignment;
    private int? _perferredWidth;
    private string _preferredWidthType;
    private string _styleId;
    private Unit? _cellSpacing;
    private Unit? _indent;
    private string _layout;
    private CellMargins _defaultCellMargins;
    private Border _borderTop;
    private Border _borderBottom;
    private Border _borderLeft;
    private Border _borderRight;
    private Border _borderInsideHorizontal;
    private Border _borderInsideVertical;

    public Border BorderInsideVertical
    {
      get { return _borderInsideVertical; }
      set { _borderInsideVertical = value; }
    }

    public Border BorderInsideHorizontal
    {
      get { return _borderInsideHorizontal; }
      set { _borderInsideHorizontal = value; }
    }

    public Border BorderRight
    {
      get { return _borderRight; }
      set { _borderRight = value; }
    }

    public Border BorderLeft
    {
      get { return _borderLeft; }
      set { _borderLeft = value; }
    }

    public Border BorderBottom
    {
      get { return _borderBottom; }
      set { _borderBottom = value; }
    }

    public Border BorderTop
    {
      get { return _borderTop; }
      set { _borderTop = value; }
    }

    public CellMargins DefaultCellMargins
    {
      get { return _defaultCellMargins; }
      set { _defaultCellMargins = value; }
    }

    public string Layout
    {
      get { return _layout; }
      set { _layout = value; }
    }

    public Unit? Indent
    {
      get { return _indent; }
      set { _indent = value; }
    }

    public Unit? CellSpacing
    {
      get { return _cellSpacing; }
      set { _cellSpacing = value; }
    }

    public string StyleId
    {
      get { return _styleId; }
      set { _styleId = value; }
    }

    public Style Style
    {
      get { return DocumentContent.GetStyle(Content, _styleId); }
    }

    public string PreferredWidthType
    {
      get { return _preferredWidthType; }
      set { _preferredWidthType = value; }
    }

    public int? PerferredWidth
    {
      get { return _perferredWidth; }
      set { _perferredWidth = value; }
    }

    public string Alignment
    {
      get { return _alignment; }
      set { _alignment = value; }
    }

    public TableProperties(Style definingStyle, XElement element)
      : this(definingStyle.ParentContent, element)
    {
      if (definingStyle == null)
        throw new ArgumentNullException("definingStyle");

      _definingStyle = definingStyle;
    }

    public TableProperties(Content parentContent, XElement element)
      : base(parentContent)
    {
      if (element == null)
      {
        _borderTop = Border.Create(this, null, null);
        _borderBottom = Border.Create(this, null, null);
        _borderLeft = Border.Create(this, null, null);
        _borderRight = Border.Create(this, null, null);
        _borderInsideHorizontal = Border.Create(this, null, null);
        _borderInsideVertical = Border.Create(this, null, null);
        return;
      }

      XNamespace ns = element.Name.Namespace;

      _alignment = XmlUtils.GetValue(element.Element(ns + "jc"));
      _perferredWidth = XmlUtils.GetValue<int>(element.Element(ns + "tblW"), "w");
      _preferredWidthType = XmlUtils.GetValue(element.Element(ns + "tblW"), "type");
      _styleId = XmlUtils.GetValue(element.Element(ns + "tblStyle"));
      _cellSpacing = ModelUtils.ToUnit(XmlUtils.GetValue<int>(element.Element(ns + "tblCellSpacing"), "w"),
        XmlUtils.GetValue(element.Element(ns + "tblCellSpacing"), "type"));
      _indent = ModelUtils.ToUnit(XmlUtils.GetValue<int>(element.Element(ns + "tblInd"), "w"),
        XmlUtils.GetValue(element.Element(ns + "tblInd"), "type"));
      _layout = XmlUtils.GetValue(element.Element(ns + "tblLayout"), "type");
      _defaultCellMargins = new CellMargins(element.Element(ns + "tblCellMar"),
        g => GetValue<object>(p => g(p._defaultCellMargins)));

      _borderTop = Border.Create(this,
        element.Elements(ns + "tblBorders").Elements(ns + "top").SingleOrDefault(),
        g => GetValue<object>(p => g(p._borderTop)));
      _borderLeft = Border.Create(this,
        element.Elements(ns + "tblBorders").Elements(ns + "left").SingleOrDefault(),
        g => GetValue<object>(p => g(p._borderLeft)));
      _borderBottom = Border.Create(this,
        element.Elements(ns + "tblBorders").Elements(ns + "bottom").SingleOrDefault(),
        g => GetValue<object>(p => g(p._borderBottom)));
      _borderRight = Border.Create(this,
        element.Elements(ns + "tblBorders").Elements(ns + "right").SingleOrDefault(),
        g => GetValue<object>(p => g(p._borderRight)));
      _borderInsideHorizontal = Border.Create(this,
        element.Elements(ns + "tblBorders").Elements(ns + "insideH").SingleOrDefault(),
        g => GetValue<object>(p => g(p._borderInsideHorizontal)));
      _borderInsideVertical = Border.Create(this,
        element.Elements(ns + "tblBorders").Elements(ns + "insideV").SingleOrDefault(),
        g => GetValue<object>(p => g(p._borderInsideVertical)));
    }

    private void AddStyleAttempts<T>(Style style, Func<TableProperties, T> valueGetter, List<Func<T>> attempts)
    {
      if (style == null)
        return;

      attempts.Add(() => valueGetter(style.TableProperties));
      if (style.BasedOnStyle != null)
        attempts.Add(() => valueGetter(style.BasedOnStyle.TableProperties));
    }

    public T GetValue<T>(Func<TableProperties, T> valueGetter)
    {
      if (valueGetter == null)
        throw new ArgumentNullException("valueGetter");

      Content parent = Content;

      List<Func<T>> attempts = new List<Func<T>>();

      attempts.Add(() => valueGetter(this));

      if (parent is Table)
      {
        Table table = (Table)parent;

        AddStyleAttempts(Style, valueGetter, attempts);
      }

      return MiscellaneousUtils.EvaluateUntilValue(attempts);
    }
  }
}
